/**
    ATClab - scriptable Air Traffic Control simulations.
    Copyright (C) 2010 openatclab@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/


#include "presenter.h"

#include <qkeysequence.h>
#include "qapplication.h"
//Added by qt3to4:
#include <QKeyEvent>
#include "task.h"
#include "trialtask.h"
#include "taskfactory.h"
#include "logger.h"
#include "engine_impl.h"

#ifdef WIN32
	#include <assert.h>
#endif

using namespace pact;

////////////////////////////////////////////////////////////////////////////////////////////////////
//
//
//
//--------------------------------------------------------------------------------------------------
//
//

Presenter* Presenter::_instance = 0;

Presenter* Presenter::instance() {
	if ( !_instance ) _instance = new Presenter();
	return _instance;
}

//--------------------------------------------------------------------------------------------------
//
//

/**
 * Constructor
 */
Presenter::Presenter( QWidget *parent, const char *name )
	  : Q3WidgetStack( parent, name )
	  , _experiment( 0 )
	  , _task()
	  , _end_task()
	  , _globals()
	  , m_globalIdx("")
{
	QFont f = font();
	f.setPointSize( 26 );
	setFont(f);
}

/**
 * Destructor
 */
Presenter::~Presenter() {
	_experiment = 0;	// not owned by this
	_instance = 0;
}

/*!
 *
 */
void Presenter::destroy() {
	delete _instance;
	_instance = 0;
}

//--------------------------------------------------------------------------------------------------
// Public Interface
//

/**
 * Set the Experiment which defines the presentation tasks.
 *
 * @param exp pointer to the experiment (does not take ownership)
 */
void Presenter::setExperiment( Experiment *exp ) {
	_experiment = exp;

	logging::KVPairs kvs;
	kvs.push_back( std::make_pair(std::string( "experiment" ), exp->root()->idx) );
	logging::Logger::instance()->info( kvs );

	//
	//
	//

	GlobalState gs = _experiment->global_state();
	GlobalStateCIt gsit;
	for ( gsit = gs.begin(); gsit != gs.end(); ++gsit ) {
		Global* var = GlobalFactory::create_variable( _experiment, *gsit );
		_globals.insert( std::make_pair(var->name(), var) );
	}

	_task     = _experiment->presentation_tasks().begin();
	_end_task = _experiment->presentation_tasks().end();
}

long 
Presenter::GetTrialTaskScore(const std::string& a_trialId) const
{
	long score = -1;

	std::map<std::string, long>::const_iterator it = m_trialScores.find(a_trialId);

	if (it != m_trialScores.end())
	{
		score = it->second;
	}

	return score;
}

/*!
 * Start the Presentation of Tasks as defined in the Experiment
 *
 * pre-condition: the experiment has been set
 */
void Presenter::start() {
	assert( _experiment != 0 );
	presentTask();
}

//--------------------------------------------------------------------------------------------------
// Private Implementaion
//

/**
 * Create and Present the next Task
 */
void Presenter::presentTask() {
	if ( _task != _end_task ) {
		// Remove any visible widgets before running the next task
		QWidget *current = visibleWidget();
			
		if ( current ) {
			removeWidget( current );
			delete current;
			current = 0;
		}

		logging::KVPairs kvs;
		kvs.push_back( std::make_pair( std::string("phase"), (*_task)->parent->idx) );
		kvs.push_back( std::make_pair( std::string("type"), (*_task)->type) );
		kvs.push_back( std::make_pair( std::string("task_id"), (*_task)->idx) );
		logging::Logger::instance()->info( kvs );

		QWidget *w = TaskFactory::createTask( _experiment, *_task );

		if (w != NULL)
		{
			// Keep a record of the score at the end of each trial task
			if ((*_task)->type == "trial")
			{
				TrialTask* trial = dynamic_cast<TrialTask*>(w);
				atc::EngineImpl* engine = dynamic_cast<atc::EngineImpl*>(trial->engine());

				m_trialTasks.push_back((*_task)->idx);
	
				connect(engine, SIGNAL(TrialScore(long)), 
						this, SLOT(LogTrialTaskScore(long)));
			}
			
			connect(w, SIGNAL(closed()), this, SLOT( presentTask() ) );
			
			addWidget(w);			// place in stack
			raiseWidget(w);			// move to top of stack
			
			++_task;
			//w->setFocus();
		}
		else
		{
			++_task;
			presentTask();
		}
	}
}


void 
Presenter::LogTrialTaskScore(long a_score)
{
	m_trialScores[m_trialTasks.at(m_trialTasks.size() - 1)] = a_score;

	_globals["score"]->set_value(a_score);

	long totalScore = a_score;


	// in case for some reason totalScore isn't a global variable, throw an exception
	
	if (_globals.find("totalScore") != _globals.end()) {
		totalScore = _globals["totalScore"]->value() + a_score;
	} else {
		throw std::runtime_error("Global variable 'totalScore' not defined in xml script.");
	}

	if (_globals.find("previousScore") == _globals.end()) {
		throw std::runtime_error("Global variable 'previousScore' not defined in xml script.");
	}


	if (m_trialTasks.size() > 1)
	{
		_globals["previousScore"]->set_value(m_trialScores[m_trialTasks.at(m_trialTasks.size() - 2)]);
	}

	_globals["totalScore"]->set_value(totalScore);
}


//--------------------------------------------------------------------------------------------------
// Private Virtual
//

/*!
 * Event handler for key press events.
 * 
 * The handler deals with the flow control of the experiment's presentation. Key
 * events not handled by the presentation task should get passed down to this 
 * handler. Only 'secret' key combinations are tested for here.
 */
void Presenter::keyPressEvent( QKeyEvent *e ) {
	int key = e->key();
	if ( e->state() & Qt::ShiftModifier )   key += Qt::SHIFT;
	if ( e->state() & Qt::ControlModifier ) key += Qt::CTRL;
	if ( e->state() & Qt::AltModifier )     key += Qt::ALT;
	if ( e->state() & Qt::MetaModifier )    key += Qt::META;
	
	QKeySequence key_sequence( key );

	if ( key_sequence == QKeySequence( "Alt+Ctrl+N" ) ) {
		presentTask();
	}
	else if ( key_sequence == QKeySequence( "Alt+Ctrl+Q" ) ) {
		close();
	} 
	else {
		e->ignore();
	}

}

////////////////////////////////////////////////////////////////////////////////////////////////////
